#ifndef __PROTOBUFFIK_STRING_H_
#define __PROTOBUFFIK_STRING_H_

#include <string>
#include "protobuffik_types.h"
#include "protobuffik_stream.h"
#include "protobuffik_results.h"
#include "protobuffik_save_load.h"


namespace protobuffik
{

template< uint_32 MaxLength = 0xFFFFFFFF >
class fixed_string : public std::string
{
public:
	fixed_string( )
	{
	}

	explicit
	fixed_string( const char* s ) :
		std::string( s )
	{
	}

	explicit
	fixed_string( const std::string& s ) :
		std::string( s )
	{
	}

	fixed_string& operator=( const char* s )
	{
		assign( s );
		return (*this);
	}

	fixed_string& operator=( const std::string& s )
	{
		assign( s.begin( ), s.end( ) );
		return (*this);
	}

	bool operator==( const char* lhv ) const
	{
		return compareWithMaxLength( lhv ) == 0;
	}

	bool operator==( const fixed_string< MaxLength >& lhv ) const
	{
		return compareWithMaxLength( lhv ) == 0;
	}

	bool operator<( const char* lhv ) const
	{
		return compareWithMaxLength( lhv ) < 0;
	}

	bool operator<( const fixed_string< MaxLength >& lhv ) const
	{
		return compareWithMaxLength( lhv ) < 0;
	}

	bool operator<=( const char* lhv ) const
	{
		return compareWithMaxLength( lhv ) <= 0;
	}

	bool operator<=( const fixed_string< MaxLength >& lhv ) const
	{
		return compareWithMaxLength( lhv ) <= 0;
	}

	bool operator>( const char* lhv ) const
	{
		return compareWithMaxLength( lhv ) > 0;
	}

	bool operator>( const fixed_string< MaxLength >& lhv ) const
	{
		return compareWithMaxLength( lhv ) > 0;
	}

	bool operator>=( const char* lhv ) const
	{
		return compareWithMaxLength( lhv ) >= 0;
	}

	bool operator>=( const fixed_string< MaxLength >& lhv ) const
	{
		return compareWithMaxLength( lhv ) >= 0;
	}

	size_t save( ostream& out ) const
	{
		size_t bytesSaved = 0;

		uint_32 n = size( );
		if( n > MaxLength )
			n = MaxLength;

		bytesSaved += save_count< MaxLength >( out, n );

		for( uint_32 i = 0; i < n; ++i )
		{
			out << (byte) operator[]( i );
			bytesSaved += sizeof( byte );
		}

		return bytesSaved;
	}

	LoadingResult load( istream& in, size_t& bytesLoaded )
	{
		clear( );

		bytesLoaded = 0;

		uint_32 n = 0;

		size_t b = 0;
		LoadingResult r = load_count< MaxLength >( in, n, b );
		if( r != LOADING_RESULT_OK )
			return r;

		bytesLoaded = b;

		if( n > MaxLength )
			n = MaxLength;

		if( in.get_unread_length( ) < n )
			return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

		for( uint_32 i = 0; i < n; ++i )
		{
			byte c = 0;
			in >> c;

			bytesLoaded += sizeof( byte );

			push_back( (char) c );
		}

		return LOADING_RESULT_OK;
	}

	LoadingResult load( istream& in )
	{
		size_t bytesLoaded = 0;
		return load( in, bytesLoaded );
	}

private:
	int compareWithMaxLength( const char* lhv ) const
	{
		uint_32 n1 = size( );
		if( n1 > MaxLength )
			n1 = MaxLength;

		return compare( 0, n1, lhv );
	}

	int compareWithMaxLength( const fixed_string< MaxLength >& lhv ) const
	{
		uint_32 n1 = size( );
		if( n1 > MaxLength )
			n1 = MaxLength;

		uint_32 n2 = lhv.size( );
		if( n2 > MaxLength )
			n2 = MaxLength;

		return compare( 0, n1, lhv, 0, n2 );
	}
};

};

#endif